

#include <keyboard.h>
#include <io.h>
#include <kmalloc.h>
#include <debug.h>
#include <devmgr.h>

static dword io_ports[1];

// circular buffer
static byte *buff;
static byte *buff_in;
static byte *buff_out;

static bool running;

/**
 * Detect the presence of a keyboard
 * 
 * returns - error
 */
int detect_keyboard(void)
{
	int ret = 0;
	
	device_t *dev = (device_t*)kmalloc(sizeof(device_t));
	legacy_locator_t *locator = (legacy_locator_t*)kmalloc(sizeof(legacy_locator_t));
	if ((dev != NULL) && (locator != NULL))
	{
		io_ports[0] = KBD_IN;
		
		dev->type = DEV_TYPE_LEGACY;
		dev->class = DEV_CLASS_INPUT;
		dev->id = "Keyboard";
		dev->gint = 9;  // ?
		dev->locator = locator;
		dev->handle = pit_handle = devmgr_create_handle();
		locator->ports = io_ports;
		locator->count = 1;
		dev->init = init_keyboard;
		dev->start = start_keyboard;
		dev->stop = stop_keyboard;
		dev->handler = keyboard_isr;
		devmgr_add_device(dev);
	}
	else
	{
		debugf("[KBD] Could not allocate device structure\n");
		ret = KBD_ERR_NOALLOC;
	}
	
	return ret;
}

/**
 * Initialize the keyboard
 * 
 * returns - error
 */
int init_keyboard(void)
{
	buff = (byte*)kmalloc(KBD_BUFFSIZE);
	if (buff == NULL)
	{
		debugf("[KBD] Could not allocate buffer\n");
		return KBD_ERR_NOALLOC;
	}
	
	buff_in = buff_out = buff;
	
	running = FALSE;
}

/**
 * Start the keyboard driver
 * 
 * returns - error
 */
int start_keyboard(void)
{
	running = TRUE;
	
	return 0;
}

/**
 * Stop the keyboard driver
 * 
 * returns - error
 */
int stop_keyboard(void)
{
	running  = FALSE;
	
	return 0;
}

/**
 * Handle keyboard interrupts
 * When the buffer is full no new keystrokes are stored
 * 
 * returns - interrupt handled? or errors
 */
int keyboard_isr(void)
{
	if ((buff_in + 1 == buff_out) || ((buff_in + 1 == KBD_BUFFSIZE) && (buff_out == 0)))
		return 0;
	
	byte key = inb(KBD_IN);
	
	buff[buff_in++] = key;
	
	if (buff_in >= KBD_BUFFSIZE)
		buff_in = 0;
	
	return 0;
}

/**
 * Get a single keystroke from the buffer
 * 
 * returns - keystroke
 */
int keyboard_get_key(byte *key)
{
	int ret = 0;
	
	if (bufF_in == buff_out)
		return KBD_ERR_BUFFEMPTY;
	
	*key = buff[buff_out++];
	
	return ret;
}

/**
 * Get a number of keystrokes from the buffer
 * 
 * byte *buffer - buffer to fill
 * dword *length - maximum number of keystrokes to get
 * 
 * returns - error
 *   also returns the number of keystrokes actually filled in in the *length field
 */
int keyboard_get_keys(byte *buffer, dword *length)
{
	int ret = 0;
	dword max_length;
	
	if (buff_in == buff_out)
		return KBD_ERR_BUFFEMPTY;
	
	if (buff_in < buff_out)
		max_length = (buff_in + KBD_BUFFSIZE) - buff_out;
	else
		max_length = buff_in - buff_out;
	
	if (*length > max_length)
		*length = max_length;
	
	int pos = 0;
	while (buff_in != buff_out)
	{
		buffer[pos] = buff[buff_out++];
		if (buff_out == KBD_BUFFSIZE)
			buff_out = 0;
	}
	
	return ret;
}
